package org.xxmi.thread;

/**
 * 此程序存在线程安全问题：打印车票时，出现重票，错票
 * 1. 线程安全问题存在的原因？
 * 由于一个线程在操作共享数据过程中，未执行完毕的情况下，另外的线程参与进来，导致共享数据存在了安全问题。
 * <p>
 * 2. 如果来解决线程的安全问题？
 * 必须让一个线程操作共享数据完毕以后，其他线程才有机会参与共享数据的操作。
 * <p>
 * 3. java 如何实现线程安全：线程的同步机制
 *      方式一：同步代码块
 *          synchronized(同步监视器){
 *          ... 需要被同步的代码块（即为操作共享数据的代码）
 *         }
 *          1. 共享数据：多个线程共同操作同一个数据（变量）
 *          2. 同步监视器：由一个类的对象来充当（这个类是任意的，如 Object obj = new Object() ），哪个线程获得此监视器，
 *              谁就可以执行大括号里被同步的代码。俗称：锁；
 *              要求：所有的线程必须共用同一把锁
 *      方式二：同步方法
 *          将需要执行共享数据的代码提取出来声明为一个方法，并在此方法加上 synchronized,即此方法是同步方法，能够保证其中一个
 *          线程执行此方法时，其他线程在外等待，直到此线程执行完此方法,
 *          注意：同步方法用的锁是当前对象 this
 *
 *  线程同步弊端：同一个时间内只能有一个线程访问共享数据，效率低下；
 *
 */
class Window2 implements Runnable {
    int ticket = 100;

    public void run() {
        while (true) {
            try {
                Thread.sleep(10);   // 睡眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 售票：" + ticket--);
            if (ticket < 0) {
                break;
            }
        }
    }
}

class Window3 implements Runnable {
    int ticket = 100;
    Object lock = new Object(); // 这个锁一定要是共用同一个

    public void run() {
        while (true) {
//            synchronized (this){    // 这里为什么可以用 this ，this 指当前对象，也就是 window 这里也只有一个 window 对象，所有可以使用this。
            synchronized (lock) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);   // 睡眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 售票：" + ticket--);
                }
            }
        }
    }
}

class Window4 implements Runnable {
    int ticket = 100;
    public void run() {
        while (true) {
            outputTicket();
        }
    }
    // 同步方法用的锁是当前对象 this
    public synchronized void outputTicket() {
        if (ticket > 0) {
            try {
                Thread.sleep(10);   // 睡眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 售票：" + ticket--);
        }
    }
}

public class TestThread2 {
    public static void main(String[] args) {
        // 出现安全问题的示例：
//        Window2 window = new Window2();

        // 避免安全问题：线程同步机制
        // 方式一：同步代码块
//        Window3 window = new Window3();

        // 方式二：同步方法
        Window4 window = new Window4();

        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.start();
        t2.start();
        t3.start();
    }


}
